{
 "cells": [
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "b023c23d-357b-434f-80df-2f5306a32bde",
   "metadata": {},
   "outputs": [],
   "source": [
    "# File: drape.ipynb\n",
    "# Code: Claude Code and Codex\n",
    "# Review: Ryoichi Ando (ryoichi.ando@zozo.com)\n",
    "# License: Apache v2.0"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "ae03e077-7c8e-4e05-a8ac-79541dab1471",
   "metadata": {},
   "outputs": [],
   "source": [
    "# import our frontend\n",
    "from frontend import App\n",
    "\n",
    "# make an app\n",
    "app = App.create(\"drape\")\n",
    "\n",
    "# create a square mesh resolution 128 spanning the xz plane\n",
    "V, F = app.mesh.square(res=128, ex=[1, 0, 0], ey=[0, 0, 1])\n",
    "\n",
    "# add to the asset and name it \"sheet\"\n",
    "app.asset.add.tri(\"sheet\", V, F)\n",
    "\n",
    "# create an icosphere mesh radius 0.5\n",
    "V, F = app.mesh.icosphere(r=0.5, subdiv_count=4)\n",
    "\n",
    "# add to the asset and name it \"sphere\"\n",
    "app.asset.add.tri(\"sphere\", V, F)\n",
    "\n",
    "# create a scene\n",
    "scene = app.scene.create()\n",
    "\n",
    "# define gap between sheets\n",
    "gap = 0.01\n",
    "\n",
    "for i in range(5):\n",
    "\n",
    "    # add the sheet asset to the scene\n",
    "    obj = scene.add(\"sheet\")\n",
    "\n",
    "    # pick two corners\n",
    "    corner = obj.grab([1, 0, -1]) + obj.grab([-1, 0, -1])\n",
    "\n",
    "    # place it with an vertical offset and pin the corners\n",
    "    obj.at(0, gap * i, 0).pin(corner)\n",
    "\n",
    "    # set fiber directions required for Baraff-Witkin\n",
    "    obj.direction([1, 0, 0], [0, 0, 1])\n",
    "\n",
    "    # set the strainlimiting of 5%\n",
    "    obj.param.set(\"strain-limit\", 0.05)\n",
    "\n",
    "# add a sphere mesh at a lower position with jitter and set it static collider\n",
    "scene.add(\"sphere\").at(0, -0.5 - gap, 0).jitter().pin()\n",
    "\n",
    "# compile the scene and report stats\n",
    "scene = scene.build().report()\n",
    "\n",
    "# preview the initial scene\n",
    "scene.preview()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "82c8bd4b-732d-4663-aa28-78667fbc0a55",
   "metadata": {},
   "outputs": [],
   "source": [
    "# create a new session with the compiled scene\n",
    "session = app.session.create(scene)\n",
    "\n",
    "# set session params\n",
    "session.param.set(\"frames\", 100).set(\"dt\", 0.01)\n",
    "\n",
    "# build this session\n",
    "session = session.build()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "1498b5f2-dd82-463a-93ab-659d41a997ad",
   "metadata": {},
   "outputs": [],
   "source": [
    "# start the simulation and live-preview the results\n",
    "session.start().preview()\n",
    "\n",
    "# also show simulation logs in realtime\n",
    "session.stream()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "9a1476ea-5daa-4eb2-8c00-b1cb4918e936",
   "metadata": {},
   "outputs": [],
   "source": [
    "session.animate()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "60a6ed08-3fa1-4bb5-aa4a-6a7e512f7dac",
   "metadata": {},
   "outputs": [],
   "source": [
    "session.export.animation()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "d3569c46-b7fe-4a62-bce6-71d0cd087131",
   "metadata": {},
   "outputs": [],
   "source": [
    "# this is for CI\n",
    "if app.ci:\n",
    "    assert session.finished()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "a695c7d8-f64e-404a-8e73-c939f6a489a7",
   "metadata": {},
   "outputs": [],
   "source": [
    "# get a list of log names\n",
    "logs = session.get.log.names()\n",
    "print(logs)\n",
    "assert \"time-per-frame\" in logs\n",
    "assert \"newton-steps\" in logs\n",
    "\n",
    "# get a list of time per video frame\n",
    "msec_per_video = session.get.log.numbers(\"time-per-frame\")\n",
    "\n",
    "# compute the average time per video frame\n",
    "print(\"avg per frame:\", sum([n for _, n in msec_per_video]) / len(msec_per_video))\n",
    "\n",
    "# get a list of newton steps\n",
    "newton_steps = session.get.log.numbers(\"newton-steps\")\n",
    "\n",
    "# compute the average of consumed newton steps\n",
    "print(\"avg newton steps:\", sum([n for _, n in newton_steps]) / len(newton_steps))\n",
    "\n",
    "# Last 8 lines. Omit for everything.\n",
    "print(\"==== log stream ====\")\n",
    "for line in session.get.log.stdout(n_lines=8):\n",
    "    print(line)"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3 (ipykernel)",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.12.3"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
